package cn.opendatachain.contract.sample;

import cn.opendatachain.common.core.ContractPayloadBuilder;
import cn.opendatachain.common.core.domain.ContractPayload;
import cn.opendatachain.common.core.domain.CustomPayload;
import cn.opendatachain.common.rsocket.ODCResponse;
import cn.opendatachain.contract.annotation.ODCContract;
import cn.opendatachain.contract.context.Context;
import cn.opendatachain.contract.context.Transaction;
import cn.opendatachain.contract.sample.entity.Dataset;
import cn.opendatachain.contract.util.JsonUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

/**
 * ODC 智能合约示例
 *
 * @author 郭志斌
 * @version dr-contract 1.0.0.RELEASE
 * <b>Creation Time:</b> 2022/9/13 15:44
 */

@Slf4j
@RestController
@Tag(name = "ODC 智能合约示例")
@ODCContract(name = "sample-contract")
public class SampleContract {

    /**
     * 智能合约上下文
     */
    private final Context context;

    /**
     * JSON 工具类
     */
    private final JsonUtils jsonUtils;

    /**
     * 构造方法 注入合约引擎上下文及其他对象,也可以使用 @Autowired 进行对象注入
     *
     * @param context   上下文
     * @param jsonUtils 工具类
     */
    public SampleContract(Context context, JsonUtils jsonUtils) {
        this.context = context;
        this.jsonUtils = jsonUtils;
    }

    /**
     * 数据上链
     *
     * @param dataset 数据上链对象
     * @return Mono<ODCResponse>
     */
    @Operation(summary = "数据上链 - 适用于普通合约")
    @PostMapping("/dataset.upsert")
    public Mono<ODCResponse> datasetUpsert(@RequestBody Dataset dataset) {
        //获取交易对象
        Transaction transaction = context.getTransaction();

        //转换为 json 数据格式
        String json = jsonUtils.toJson(dataset);
        log.debug("获取原始上链数据 json ：{}", json);

        // 省略获取 Dataset 后数据的校验

        //构建数据上链相关参数及签名
        ContractPayload payload = transaction.payloadBuilder(dataset.getCstr(), json);

        log.debug("ContractPayload ： {}", jsonUtils.toJson(payload));

        // 数据上链
        return transaction.upsert(payload);
    }

    /**
     * 批量数据上链 - 如何调用智能合约接口详见智能合约进阶章节
     *
     * @param datasets 数据上链对象
     * @return Mono<ODCResponse>
     */
    @Operation(summary = "批量数据上链 - 适用于普通合约")
    @PostMapping("/dataset.upsert.batch")
    public Publisher<ODCResponse> datasetUpsertBatch(@RequestBody List<Dataset> datasets) {
        //获取交易对象
        Transaction transaction = context.getTransaction();

        //获取批量数据并转换为可上链对象 ContractPayload 列表
        List<ContractPayload> payloads = datasets.stream().map(ds ->
                transaction.payloadBuilder(ds.getCstr(), jsonUtils.toJson(ds))
        ).collect(Collectors.toList());

        log.debug("ContractPayloads size is : {}", payloads.size());

        // 批量数据上链
        return transaction.upsertBatch(payloads);
    }

    /**
     * 数据上链
     *
     * @param payload 数据上链对象
     * @return Mono<ODCResponse>
     */
    @Operation(summary = "单条数据上链 - 适用于通用合约")
    @PostMapping("/data.upsert")
    public Mono<ODCResponse> upsert(@RequestBody ContractPayload payload) {
        //获取交易对象
        Transaction transaction = context.getTransaction();

        log.info("ContractPayload ： {}", jsonUtils.toJson(payload));

        // 数据上链
        return transaction.upsert(payload);
    }

    /**
     * 批量数据上链 - 推荐使用
     *
     * @param payloads 数据上链对象列表
     * @return Publisher<ODCResponse>
     */
    @Operation(summary = "批量数据上链 - 适用于通用合约")
    @PostMapping("/data.upsert.batch")
    public Publisher<ODCResponse> upsertBatch(@RequestBody List<ContractPayload> payloads) {
        //获取交易对象
        Transaction transaction = context.getTransaction();

        log.info("ContractPayloads size is : {}", payloads.size());

        // 数据上链
        return transaction.upsertBatch(payloads);
    }

    /**
     * 历史数据查询
     *
     * @param uid 数据标识,推荐使用 cstr
     * @return Mono<ODCResponse>
     */
    @Operation(summary = "历史数据查询")
    @GetMapping("/data.history")
    public Mono<ODCResponse> history(@RequestParam(name = "uid") String uid) {
        //获取交易对象
        Transaction transaction = context.getTransaction();

        //构建数据查询相关参数及签名
        ContractPayload payload = transaction.payloadQueryBuilder(uid);

        log.info("ContractPayload ： {}", jsonUtils.toJson(payload));

        // 查询历史
        return transaction.history(payload);

    }

    /**
     * 获取状态数据查询
     *
     * @param uid 数据标识,推荐使用 cstr
     * @return Mono<ODCResponse>
     */
    @Operation(summary = "获取最新状态数据")
    @GetMapping("/data.latestStatus")
    public Mono<ODCResponse> latestStatus(@RequestParam(name = "uid") String uid) {
        //获取交易对象
        Transaction transaction = context.getTransaction();

        //构建数据查询相关参数及签名
        ContractPayload payload = transaction.payloadQueryBuilder(uid);

        log.info("ContractPayload ： {}", jsonUtils.toJson(payload));

        return transaction.latestStatus(payload);
    }

}
